﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NeturalMath.Properties;
using NeturalMath.Units;

namespace NeturalMath.Expressions
{
    public class UnitMeasureVisitor:MathExpressionVisitor<UnitMeasure>
    {
        public UnitMeasureVisitor(MathRuntime runtime) 
            : base(runtime)
        {
        }

        protected override UnitMeasure VisitAssignment(AssignmentExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation,Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitBinaryOperator(BinaryOperatorExpression expression)
        {
            var left = Visit(expression.Left);

            switch (expression.OperatorType)
            {
                case BinaryOperatorType.Multiply:
                    var rightMultiply = Visit(expression.Right);
                    return left *rightMultiply;

                case BinaryOperatorType.Divide:
                    var rightDivide = Visit(expression.Right);
                    return left / rightDivide;

                case BinaryOperatorType.Power:
                    var right = expression.Right as ConstantValueExpression;
                    if (right == null)
                        throw new MathException(ErrorCodes.UnitMagnitideNotConstant, Resources.UnitMagnitudeNotConstant);

                    var numValue = right.Value as NumberValue;
                    if (numValue==null )
                        throw new MathException(ErrorCodes.UnitMagnitideNotConstant, Resources.UnitMagnitudeNotConstant);
                    else if (!numValue.Value.IsInteger())
                        throw new MathException(ErrorCodes.UnitMagnitideNotConstant, Resources.UnitMagnitudeNotConstant);

                    return UnitMeasure.PowerOperator(left, (int) numValue.Value);
                default:
                    throw new MathException(ErrorCodes.InvalidUnitOperator, Resources.InvalidUnitOperator);
            }
        }

        protected override UnitMeasure VisitConstant(ConstantValueExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitDomain(DomainExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitFunctionCall(FunctionUseExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitImportKeyword(ImportKeywordExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitMemberLookup(LookupExpression expression)
        {
            var dimensionName = expression.MemberName;
            var dimension = Runtime.Measurements.GetDimension(dimensionName);

            if (dimension == null)
                throw new MathException(ErrorCodes.CouldNotLookupUnitDimension, Resources.CouldNotLookupUnitDimension);

            var measure = new UnitMeasure(dimension);
            return measure;
        }

        protected override UnitMeasure VisitDefKeyword(DefKeywordExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitPrintKeyword(PrintKeywordExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitParameterExpression(FunctionParameterExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitUnaryOperator(UnaryOperatorExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitDelegate(DelegateExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }

        protected override UnitMeasure VisitSymbolicLookup(SymbolicLookupExpression expression)
        {
            throw new MathException(ErrorCodes.InvalidOperation, Resources.InvalidCallToFunction);
        }
    }
}
